#!/usr/bin/python
# -*- coding: utf-8 -*-

from utils import to_utf8, now
import re
import sys
import copy
import json
# from time import time as now
from decimal import Decimal

class FieldValue(object):
	
	def __init__(self, text):
		self._value = text
	
	def __str__(self):
		return self.value
	
	@property
	def value(self):
		if self._value is not None:
			return self._value
		return ''

	@property
	def int_value(self):
		val = self.value
		if val.isdigit():
			return int(val)
		return 0
	
	@property
	def long_value(self):
		val = self.value
		if val.isdigit():
			return long(val)
		return 0L

	@property
	def bool_value(self):
		pass

#-------------- widget ----------------
class Widget(object):
	
	def __init__(self, field=None, attrs=None):
		self.field = field
		self.attrs = {}
		if attrs is not None:
			self.attrs.update(attrs)
	
	def render(self):
		raise Exception("Not implemented.")
	
	def __str__(self):
		return self.render()
	
	def get_attr(self, name):
		return self.attrs.get(name) or None
	
	def get_attr_int(self, name):
		attr = self.get_attr(name)
		if attr is None:
			return 0
		elif isinstance(attr, int):
			return attr
		return int(attr) if attr.isdigit() else 0
	
	def set_attr(self, name, value):
		if name:
			value = to_utf8(value)		
			self.attrs[name] = value

class TextWidget(Widget):
	
	def __init__(self, field=None, attrs=None):
		super(TextWidget, self).__init__(field, attrs)
	
	def render(self):
		if self.field is not None:
			controlid = self.field.get_id()
			controlname = self.field.name
			value = self.field.value
			size = self.get_attr_int('size')
			multiline = self.get_attr('multiline')
			readonly = self.get_attr('readonly')
			input_type = self.get_attr('type') or 'text'
			if multiline:
				cols = self.get_attr_int('cols')
				rows = self.get_attr_int('rows')
				if cols<=0:
					cols=30
				if rows<=1:
					rows=1
				return u'''<textarea id='%s' name='%s' cols='%s' rows='%s'%s>%s</textarea>''' % (
							controlid, controlname, cols, rows, 
							'' if not readonly else ' readonly class="readonly" ',
							value)
			else:
				if size>0:
					return u"<input type='%s' id='%s' name='%s'%s value=\"%s\" size='%s' />" % (
							input_type, controlid, controlname, 
							'' if not readonly else ' readonly class="readonly" ',
							value, size)
				else:
					return u"<input type='%s' id='%s' name='%s'%s value=\"%s\" />" % (
							input_type, controlid, controlname, 
							'' if not readonly else ' readonly class="readonly" ',
							value)
		return ''


class FileUploadWidget(Widget):
	def render(self):
		if self.field:
			controlname = self.field.name
			return u"<input type='file' name='%s' />" % controlname
		return u''

class FileUploadWidget_WithPicture(FileUploadWidget):
	def render(self):
		if self.field:
			controlid = self.field.get_id()
			controlname = self.field.name
			picurl = to_utf8(self.get_attr('picurl'))
			piclinkurl = to_utf8(self.get_attr('piclinkurl'))
			picdeleteurl = to_utf8(self.get_attr('picdeleteurl'))
			pic_width = to_utf8(self.get_attr_int('picwidth'))
			pic_height = to_utf8(self.get_attr_int('picheight'))
			html = []
			if picurl:
				html.append(u'<div>')
				if piclinkurl:
					html.append(u'<a href="%s" target="_blank">' % piclinkurl)
				html.append(u'<img src="%s" border="0" height="30" align="absmiddle" />' % picurl)
				if piclinkurl:
					html.append(u"</a>")
				if picdeleteurl:
					html.append(u"&nbsp; ")
					func_id = u'%s_delpic_%s' % (controlid, int(now()))
					html.append(u"<script language='javascript'>function %s(){if(confirm('确认去除已有图片？')){ location.href='%s'; }}</script>" % (func_id, picdeleteurl))
					html.append(u'<a href="javascript:%s();">去除图片</a>' % func_id)
				html.append(u'</div>')
			html.append(u"<input type='file' name='%s' />" % controlname)
			return u''.join(html)
		return u''

class SingleChoiceWidget(Widget):
	
	def __init__(self, field, attrs=None):
		super(SingleChoiceWidget, self).__init__(field, attrs)
	
	def render(self):
		if self.field is not None:
			f  = self.field
			html = []
			html.append(u'<select name="%s" id="%s"' % (f.name, f.get_id()))
			html.append(u'>')
			if f.choices is not None:
				for item in f.choices:
					html.append(u'<option value="%s"' % to_utf8(item[0]))
					if to_utf8(f.value) == to_utf8(item[0]):
						html.append(u' selected ')
					html.append(u'>%s</option>' % to_utf8(item[1]))
			html.append(u'</select>')
			return u''.join(html)
		return u''

class RadioGroupWidget(Widget):
	
	def render(self):
		if self.field is not None:
			f  = self.field
			html = []
			choices = f.choices
			if not choices:
				# raise Exception('Attribute "choices" of the field must be specified.')
				choices = []
			index = 1
			for item in choices:
				checked = to_utf8(f.value)==to_utf8(item[0])
				ctlid = u'%s_%s' % (f.get_id(), index)
				label = to_utf8(item[1]) if len(item)>1 else to_utf8(item[0])
				html.append(u'<span class="sf-radio-span">')
				html.append(u'<input type="radio" name="')
				html.append(to_utf8(f.name))
				html.append(u'" id="')
				html.append(ctlid)
				html.append(u'" value="')
				html.append(to_utf8(item[0]))
				html.append(u'"')
				html.append(u'checked ' if checked else u'')
				html.append(u"/>")
				html.append(u'<label for="%s"' % ctlid)
				html.append(u'>%s</label>' % label)
				html.append(u'</span>')
				index += 1
			return u''.join(html)
		return u''

class CheckBoxGroupWidget(Widget):
	def render(self):
		height = self.get_attr_int('size')
		height = 3 if height<=0 else height
		if self.field is not None:
			f = self.field
			controlid = f.get_id()
			controlname = f.name
			value = f.value
			sys.stdout.write(u'\n'.join(value))
			if f.choices:
				html = []
				i = 1
				for item in f.choices:
					itemid = u'%s_%s' % (controlid, i)
					html.append(u'<span class="sf-boxes-group">')
					html.append(u'<input type="checkbox" name="%s"' % controlname)
					html.append(u' id="%s" value="%s"' % (itemid, to_utf8(item[0])))
					if item[0] in value:
						html.append(u' checked')
					html.append(u'/>')
					html.append(u'<label for="%s">' % itemid)
					html.append(to_utf8(item[1]))
					html.append(u'</label>')
					html.append(u'</span>')
					i = i+1
				return u''.join(html) 
		return u''


class LocationWidget(Widget):
	def render(self):
		ctlid = self.field.get_id()
		name = self.field.name
		value = self.field.value
		return u'''<textarea style='display:none;' name='%s' id='%s'>%s</textarea>
			<select id='%s'></select><select id='%s'></select>
			<script language='javascript'>city_selector.config.value_format='json';
			city_selector.attach("%s","%s","%s", "%s","%s");</script>
			''' % (name, ctlid, value,
				ctlid+"_province", #self.render_options(choices=[], selected=""),
				ctlid+"_city", #self.render_options(choices=[], selected=""),
				ctlid, ctlid+"_province", ctlid+"_city", value.province, value.city
			)

class PasswordWidget(Widget):
	def render(self):
		ctlid = self.field.get_id()
		name = self.field.name
		value = self.field.value
		return u'''<input type="password" name="%s" id="%s" value="%s" />''' % (
			name, ctlid, value)


class HtmlEditorWidget(Widget):
	def render(self):
		ctlid = self.field.get_id()
		name = self.field.name
		value = self.field.value
		csrftoken = self.field.form.csrf_value if hasattr(self.field.form, 'csrf_value') else ''
		uploadchannel = self.field.uploadchannel if hasattr(self.field, 'uploadchannel') else ''
		if not uploadchannel:
			uploadchannel = self.get_attr('uploadchannel')
		uploadurl = self.get_attr('uploadurl')
		if uploadchannel == 'cms_post':
			uploadurl = '/manage/cms/photo_upload_json/'
		elif uploadchannel == 'product_details':
			pass
		elif uploadchannel == 'store_documents':
			pass
		else:
			# raise Exception('Unknown upload channel value: "%s"' % uploadchannel)
			pass
		if not uploadurl:
			raise Exception('The htmleditor widget needs a non-empty "uploadurl" attribute.')
		width = self.get_attr('width')
		width = 'width:100%;' if not width else 'width:%s;' % width
		height = self.get_attr('height')
		height = 'height:300px;' if not height else 'height:%s;' % height
		html = []
		html.append('<textarea name="%s" id="%s" ' % (name, ctlid))
		html.append('style="%s%s" ' % (width, height))
		html.append('>')
		html.append(value)
		html.append('</textarea>')
		html.append('<script language="javascript">')
		html.append('KE.show({"id": "%s", "uploadurl": "%s", "csrfmiddlewaretoken": "%s"});' % (ctlid, uploadurl, csrftoken))
		html.append('</script>')
		return ''.join(html)

#-------------- field ----------------
class Field(object):
	
	_index = 0
	
	def __init__(self, label='', widget=None, html='', 
				help_text='', choices=None, initial='',
				 spliter=False, hidden=False, form=None):
		self.index = Field._index + 1
		Field._index += 1
		self.label = to_utf8(label)
		if widget is not None:
			if isinstance(widget, type) and callable(widget):
				self.widget = widget(self)
			elif isinstance(widget, Widget):
				self.widget = widget
		else:
			self.widget = None
		if self.widget is None:
			self.widget = TextWidget(self)
		if self.widget is not None:
			self.widget.field = self
		self.html = to_utf8(html)
		self.help_text = to_utf8(help_text)
		self.choices = choices
		self.name = '' # this value is set by parent-container-form
		self.initial = initial # default value of the field
		self._value = None
		self.spliter = spliter
		self.hidden = hidden
		if self.initial is not None:
			self.value = self.initial
		self.form = form

	def get_id(self):
		return "id_%s" % self.name
	
	def practical_value(self, value):
		'''virtual, can be overrided'''
		return to_utf8(value)
		#return value
	
	@property
	def value(self):
		return self._value
	
	@value.setter
	def value(self, val):
		self._value = self.practical_value(val)

	def __str__(self):
		if self.html is not None:
			if self.html:
				r_id = re.compile(r'%id')
				r_name = re.compile(r'%name')
				r_value = re.compile(r'%value')
				r_widget = re.compile(r'%widget')
				s = self.html
				s = r_id.sub(self.get_id(), s)
				s = r_name.sub(self.name, s)
				s = r_value.sub(to_utf8(self.value), s)
				if self.widget:
					s = r_widget.sub(self.widget.render(), s)
				return s
		if self.spliter:
			return self.label
		return self.widget.render()
	
	def helptext_or_error(self):
		if self.form:
			if self.form.errors.has_key(self.name):
				err = self.form.errors[self.name]
				if err:
					return '<span class="error err">%s</span>' % err
		if self.help_text:
			return self.help_text
		return ''

class IntField(Field):	

	def practical_value(self, value):
		if isinstance(value, int):
			return value
		value = to_utf8(value)
		if value.isdigit():
			return int(value)
		if len(value)>1:
			if value[0] == '-' and value[1:].isdigit():
				return int(value)
		return 0

class LongField(Field):	

	def practical_value(self, value):
		if isinstance(value, long):
			return value
		value = to_utf8(value)
		if value.isdigit():
			return long(value)
		return 0L

class DecimalField(Field):

	def practical_value(self, value):
		if isinstance(value, Decimal):
			return value
		value = to_utf8(value)
		try:
			return Decimal(value)
		except:
			pass
		return Decimal()


class ListField(Field):
	
	def __init__(self, *args, **kargs):
		if not kargs.has_key('widget'):
			kargs['widget'] = CheckBoxGroupWidget
		super(ListField, self).__init__(*args, **kargs)

	def practical_value(self, value):
		if value:
			result = [part.strip() for part in value.split(',') if len(part.strip())>0]
			return result
		return []

class HtmlField(Field):
	def __init__(self, *args, **kargs):
		if not kargs.has_key('widget'):
			kargs['widget'] = HtmlEditorWidget
		if kargs.has_key('uploadchannel'):
			self.uploadchannel = kargs['uploadchannel']
			kargs.pop('uploadchannel')
		else:
			raise Exception('''Parameter 'uploadchannel' must be specified, the value can be "cms_post|product".''')
		super(HtmlField, self).__init__(*args, **kargs)

class WarningTextField(Field):
	
	def __init__(self, text):
		super(WarningTextField, self).__init__(label='', spliter=True)
		self.text = text
	
	@property
	def text(self):
		return self._text
	
	@text.setter
	def text(self, text):
		text = to_utf8(text)
		self._text = text
		self.html = u'<span class="warning">%s</span>' % text

class BoolField(Field):
	def __init__(self, *args, **kargs):
		if not kargs.has_key('widget'):
			kargs['widget'] = RadioGroupWidget # SingleChoiceWidget
		if not kargs.has_key('choices'):
			kargs['choices'] = ((True, '是'),(False, '否'))
		super(BoolField, self).__init__(*args, **kargs)

	def practical_value(self, value):
		if value is None:
			return False
		if isinstance(value, bool):
			return value
		value = to_utf8(value)
		if value.isdigit():
			intval = int(value)
			return True if intval>0 else False
		if value.lower()=='true':
			return True
		return False


class FileUploadField(Field):

	def __init__(self, *args, **kargs):
		if not kargs.has_key('widget'):
			kargs['widget'] = FileUploadWidget
		super(FileUploadField, self).__init__(*args, **kargs)
	
	def practical_value(self, value):
		if isinstance(value, UploadedFile):
			return value
		return None

class LocationField(Field):
	def __init__(self, *args, **kargs):
		if not kargs.has_key('widget'):
			kargs['widget'] = LocationWidget
		super(LocationField, self).__init__(*args, **kargs)
	
	def practical_value(self, value):
		if value is not None:
			value = to_utf8(value)
			if isinstance(value, str):
				if value.startswith('{'):
					# json
					obj = json.loads(value)
					return LocationInfo(country=to_utf8(obj['country']), 
								province=to_utf8(obj['province']), 
								city=to_utf8(obj['city']), 
								district=to_utf8(obj['district']))
				else:
					if value.find('\n')<0:
						value = '%s\n' % value
					parts = [part.strip() for part in value.split('\n')]
					return LocationInfo(province=parts[0], city=parts[1])
			if isinstance(value, LocationInfo):
				return value
		return LocationInfo()


def field_compare(x, y):
	if x.index > y.index:
		return 1
	elif x.index<y.index:
		return -1
	return 0

#-------------- form ----------------

class DeclarativeFieldsMetaclass(type):
	
	def __new__(cls, name, bases, attrs):
		form = super(DeclarativeFieldsMetaclass, cls).__new__(cls, name, bases, attrs)
		return form

class KeyCaseInsensitiveDict(dict):
	
	def __contains__(self, key):
		if key is None:
			key = ''
		return super(KeyCaseInsensitiveDict, self).__contains__(key.lower())
	
	def get(self, key):
		return super(KeyCaseInsensitiveDict, self).get(key.lower()) 
	
	def __getitem__(self, key):
		if key is None:
			key = ''
		return super(KeyCaseInsensitiveDict, self).__getitem__(key.lower())
	
	def __setitem__(self, key, value):
		if key is None:
			key = ''
		super(KeyCaseInsensitiveDict, self).__setitem__(key.lower(), value)
	
	def update(self, items):
		for k in items.keys():
			self[k] = items[k]

class BaseForm(object):
	
	def update_values_by_request(self, request):
		if request is not None:
			if request.POST is not None:
				self.initial_values.update(request.POST)
			if self.method.lower()=='get' and request.GET:
				self.initial_values.update(request.GET)
		if self.initial_values and hasattr(self, 'fields'):
			for k in self.initial_values:
				f = self.get_field(k)
				if f:
					f.value = self.initial_values[k]
	
	def __init__(self, values=None, files=None, formid='', name='', submitlabel='',
				 onsubmit='', hidden=False, request=None, putbuttonsaside=False, 
				 redirect=None, kb=None, forwardlinks=None, multipart=False,
				 singlerow=False, singlerow_with='', method='POST', action=''):
		self.errors = KeyCaseInsensitiveDict()
		self.formid = to_utf8(formid)
		self.name = to_utf8(name)
		self.method = method
		self.action = action
		self.multipart = multipart
		self.submitlabel = to_utf8(submitlabel)
		self.onsubmitscript = to_utf8(onsubmit)
		self.hidden = hidden
		self.request = request
		self.result = {'result': False, 'msg': ''}
		self.putbuttonsaside = putbuttonsaside
		self.redirect = redirect
		self.kb = kb
		self.forwardlinks = forwardlinks
		self.singlerow = singlerow # 如果为True，则submit按钮和控件在同一行
		self.singlerow_with = singlerow_with # 和submit按钮同一行显示的的控件名
		#
		self.initial_values = KeyCaseInsensitiveDict()
		if values is not None:
			self.initial_values.update(values)
		else:
			self.update_values_by_request(request)
		if files is None and request is not None:
			# self.files = request.FILES
			if hasattr(request, 'FILES'):
				self.files = request.FILES
			elif hasattr(request, 'files'):
				self.files = request.files
			else:
				self.files = None
		else:
			self.files = files
		# parse fields
		d = self.__class__.__dict__
		meta_fields = [(d[k],k) for k in d if isinstance(d[k], Field)]
		# raise Exception(', '.join([f[1] for f in meta_fields]))
		for mf in meta_fields:
			mf[0].name = mf[1].lower()
		self.base_fields = [f[0] for f in meta_fields]
		self.base_fields.sort(field_compare)
		self.fields = copy.deepcopy(self.base_fields)
		# sync initial values
		for f in self.fields:
			f.form = self
			init_val = self.initial_values.get(f.name) or None
			if init_val is not None:
				f.value = init_val
		if self.files:
			if len(self.files):
				for fieldname in self.files:
					f = self.files[fieldname]
					field = self.get_field(fieldname)
					if f and field:
						field.value = UploadedFile(f)
	
	def get_field(self, name):
		if self.fields is not None:
			for f in self.fields:
				if f.name.lower() == name.lower():
					return f
		return None

	#def get_value(self, name):
	#	f = self.get_field(name)
	#	if f is not None:
	#		return FieldValue(f.value)
	#	return FieldValue('')
	
	def update_values(self, values):
		if values is not None:
			if isinstance(values, dict):
				for k in values.keys():
					f = self.get_field(k)
					if f:
						f.value = values[k]
	
	@property
	def data(self):
		if hasattr(self, '_data'):
			return self._data
		d = KeyCaseInsensitiveDict()
		for f in self.fields:
			d[f.name] = f.value
		self._data = d
		return d

	def get_file(self, name):
		if self.files is not None:
			f = self.files.get(name) or None
			if f is not None:
				return UploadedFile(f)
		return None
		
	def get_id(self):
		if self.formid:
			return self.formid
		return "id_%s" % self.name

	def __str__(self):
		try:
			return self.html_table()
		except Exception:
			return "Exception while rendering: %s" % sys.exc_info()[1]
	
	def render_result(self):
		html = []
		if self.result is not None:
			if len(self.result['msg'])>0:
				if self.result['result']:
					html.append(u"<span class='success'>")
				else:
					html.append(u"<span class='fail'>")
				html.append(u"%s" % to_utf8(self.result['msg']))
				html.append(u"</span>")
		return u''.join(html)
	
	@property
	def html_result(self):
		return self.render_result()

	def render_form_head(self):
		html = []
		# html.append('%s' % self.objectid)
		html.append(u'''<form method='%s' ''' % self.method)
		formid = self.get_id()
		if formid:
			html.append(u'''id='%s' ''' % formid)
		if len(self.name)>0:
			html.append(u'''name='%s' ''' % self.name)
		if len(self.action)>0:
			html.append(u'''action='%s' ''' % self.action)
		if len(self.onsubmitscript)>0:
			html.append(u'''onsubmit="%s" ''' % self.onsubmitscript)
		if self.multipart:
			html.append(u'''enctype="multipart/form-data" ''')
		if self.hidden:
			html.append(u"style='display:none;'>")
		else:
			html.append(u">")
		return u''.join(html)
	
	def render_redirect(self):
		html = []
		if isinstance(self.redirect, str):
			html.append(u"<span class='waiting'>请稍等...</span><script language='javascript'>window.setTimeout(function(){ location.href='%s'; }, 900);</script>" % to_utf8(self.redirect))
		elif isinstance(self.redirect, tuple) or isinstance(self.redirect, list):
			if len(self.redirect)>=2:
				url = self.redirect[0]
				label = self.redirect[1]
				ms = 900
				if len(self.redirect)>=3:
					if isinstance(self.redirect[2], int):
						ms = self.redirect[2]
					elif self.redirect[2].isdigit():
						ms = int(self.redirect[2])
				html.append(u"<span class='waiting'>%s</span><script language='javascript'>window.setTimeout(function(){ location.href='%s'; }, %s);</script>" % (to_utf8(label), to_utf8(url), ms))
			else:
				html.append(u"<span class='waiting'>请稍等...</span><script language='javascript'>window.setTimeout(function(){ location.href='%s'; }, 900);</script>" % to_utf8(self.redirect))
		return ''.join(html)

	@property
	def html_redirect(self):
		return self.render_redirect()
	
	def render_forwardlinks(self):
		html = []
		if len(self.forwardlinks)>0:
			if self.putbuttonsaside:
				html.append(u"<div class='forwardlinks forwardlinks-aside'>")
			else:
				html.append(u"<div class='forwardlinks forwardlinks-aside gotop'>")
			for item in list(self.forwardlinks):
				if isinstance(item, tuple) or isinstance(item, list):
					if len(item)==2:
						url = to_utf8(item[0])
						text = to_utf8(item[1])
						html.append(u"<a href='%s'>%s</a>" % (url, text))
					else:
						html.append(u"<a href='%s'>%s</a>" % (to_utf8(item[0]), to_utf8(item[0])))
				elif isinstance(item, str):
					html.append(u"<a href='%s'>%s</a>" % (to_utf8(item), to_utf8(item)))
				elif isinstance(item, dict):
					html.append(u"<a href='%s'>%s</a>" % (to_utf8(item['url']), to_utf8(item['text'])))
			html.append(u"</div>")
		return u''.join(html)

	def render_kb(self):
		html = []
		if self.kb is not None:
			if len(self.kb)>0:
				html.append(u"<div class='kb'>")
				for kbnode in self.kb:
					html.append(u'<span class="node">%s</span>' % to_utf8(kbnode))
				html.append(u"</div>")
		return u''.join(html)

	def render_form_tail(self):
		html = []
		html.append(u'</form>')
		return u''.join(html)
	
	@property
	def as_p_html(self):
		if not hasattr(self, '_as_p_html'):
			self._as_p_html = self.as_p()
		return self._as_p_html

	def as_p(self):
		submit_appended = False
		html= []
		html.append(self.render_result())
		if not self.result['result']:
			html.append(self.render_form_head())
			html.append(u"<div class='form'>")
			for f in self.fields:
				if f.spliter:
					html.append(u"<div class='row spliter'>")
					html.append(u"<span>%s</span>" % f)
					html.append(u"</div>")
				elif not f.hidden:
					html.append(u"<div class='row'>")
					if f.label:
						html.append(u"<span class='label'>%s: </span>" % f.label)
					#else:
					#	html.append(u"<td>&nbsp;</td>")
					err = self.errors.get(f.name) or ''
					if err:
						html.append(u"<span class='error err'>%s</span>" % to_utf8(err))
					html.append(u"<div class='controls'>")
					html.append(u"%s" % f)
					if not submit_appended and self.singlerow and self.submitlabel:
						if f.name == self.singlerow_with or not self.singlerow_with:
							# html.append(u'</td><td>')
							html.appendu("<input type='submit' value='%s' class='submit' />" % self.submitlabel)
							submit_appended = True
					if f.help_text:
						html.append(u'<span class="help_text">%s</span>' % f.help_text)
					html.append(u"</div>")
					html.append(u"</div>")
			if self.submitlabel and not submit_appended:
				html.append(u"<div class='row'>")
				if self.putbuttonsaside:
					html.append(u"<div class='btns-aside'>")
				else:
					html.append(u"<div class='btns'>")
				html.append(u"<input type='submit' value='%s' class='submit' />" % self.submitlabel)
				html.append(u"</div>")
				html.append(u"</div>")
				submit_appended = True
			html.append(u'</div>') 
			html.append(u'<div class="hiddenfields" style="display:none;">')
			for f in self.fields:
				if f.hidden:
					html.append(u"%s" % f)
			html.append(u'</div>')
			html.append(self.render_form_tail())
		if self.redirect:
			html.append(self.render_redirect())
		elif self.forwardlinks:
			html.append(self.render_forwardlinks())
		if self.kb:
			html.append(self.render_kb())
		return u''.join(html)

	def html_table(self):
		submit_appended = False
		html= []
		html.append(self.render_result())
		if not self.result['result']:
			html.append(self.render_form_head())
			html.append(u"<table class='form'>")
			for f in self.fields:
				if f.spliter:
					html.append(u"<tr>")
					html.append(u"<td colspan='2'>%s</td>" % f)
					html.append(u"</tr>")
				elif not f.hidden:
					html.append(u"<tr>")
					if f.label:
						html.append(u"<th>%s: </th>" % f.label)
					else:
						html.append(u"<td>&nbsp;</td>")
					html.append(u"<td>")
					err = self.errors.get(f.name) or ''
					if err:
						html.append(u"<div class='error err'>%s</div>" % to_utf8(err))
					html.append(u"%s" % f)
					if not submit_appended and self.singlerow and self.submitlabel:
						if f.name == self.singlerow_with or not self.singlerow_with:
							html.append(u'</td><td>')
							html.appendu("<input type='submit' value='%s' class='submit' />" % self.submitlabel)
							submit_appended = True
					if f.help_text:
						html.append(u'<span class="help_text">%s</span>' % f.help_text)
					html.append(u"</td>")
					html.append(u"</tr>")
			if self.submitlabel and not submit_appended:
				html.append(u"<tr><td>&nbsp;</td>")
				if self.putbuttonsaside:
					html.append(u"<td><div class='btns-aside'>")
				else:
					html.append(u"<td><div class=''>")
				html.append(u"<input type='submit' value='%s' class='submit' />" % self.submitlabel)
				html.append(u"</div></td>")
				html.append(u"</tr>")
				submit_appended = True
			html.append(u'</table>')
			html.append(u'<div class="hiddenfields" style="display:none;">')
			for f in self.fields:
				if f.hidden:
					html.append(u"%s" % f)
			html.append(u'</div>')
			html.append(self.render_form_tail())
		if self.redirect:
			html.append(self.render_redirect())
		elif self.forwardlinks:
			html.append(self.render_forwardlinks())
		if self.kb:
			html.append(self.render_kb())
		return u''.join(html)
	
	def validate(self):
		raise Exception("Not implemented.")

class Form(BaseForm):	
	
	def __init__(self, *args, **kargs):
		super(Form, self).__init__(*args, **kargs)
	
	@property
	def success(self):
		if self.result['result'] and not self.errors:
			return True
		return False
	
	#def __getattr__(self, key):
	#	return super(Form, self).__getattr__(key)

#-------------- test ----------------
class TestForm(Form):
	keywords = Field(label=u"关键词", initial="xx")


if __name__=='__main__':
	form = TestForm({'keywords': '333'})
	# form.get_field("keywords").value = "haha"
	print form
	print form.get_value("keywords").int_value
	
	